fromCrypto.PublicKeyimportRSAfromCrypto.Util.numberimportinverse,getRandomInteger,getStrongPrime,isPrimefrommathimportgcddefprime_product_constant(max_const=65537):print("Calculating Prime Const")i=5ret=1*2*3whilei<max_const:ifisPrime(i):ret*=ii+=2returnretdefverifier_part1(size,num_challenges):#Randomly generate challenges
return[getRandomInteger(size//2)for_inrange(num_challenges)]defprover_part2(size,challenges):#Generate Key
print("Generating Key")mykey=RSA.generate(size)private_p, private_q, public_n, private_phi=[mykey.p,mykey.q,mykey.n,(mykey.p-1)*(mykey.q-1)]#Calculate the inverse for the power
m=inverse(public_n,private_phi)#Calculate Proofs
# challenge ^ m % N
return[public_n,[pow(chal,m,public_n)forchalinchallenges]]defverifier_part3(challenges,proofs,public_n,alpha_const=319567):#Check Varables
assertpublic_n>1#Check that N is not devisible by primes smaller than 65537, or 319567
prime_product=prime_product_constant(alpha_const)assertgcd(prime_product,public_n)==1#Check that proof is greater than 0
forproofinproofs:assert(proof%public_n)>0#Validate Challenges
foridxinrange(num_challenges):#Compute test
tmp=pow(proofs[idx],public_n,public_n)#Check that chal^m = proof^N
#print(f"Testing Proof {challenges[idx]}, {tmp}")
asserttmp==challenges[idx]returnTrueif__name__=='__main__':#Setup
num_challenges=5size=1024#Get challenges from Verifier
challenges=verifier_part1(size,num_challenges)#Generate Profs from the challenges
public_n, proofs=prover_part2(size,challenges)#From the proofs verify that they are correct
valid=verifier_part3(challenges,proofs,public_n)print(f"Challenges have been confirmed: {valid}")
The main part of this protocol is using a common random number generator so that it is unique and static for each side
Implementation:
fromCrypto.PublicKeyimportRSAfromCrypto.Util.numberimportinverse,getRandomInteger,getStrongPrime,isPrimefrommathimportgcdimporthashlibdefprime_product_constant(max_const=65537):print("Calculating Prime Const")i=5ret=1*2*3whilei<=max_const:ifisPrime(i):ret*=ii+=2returnretdefgenerate_challenges(size,public_n,num_challenges,salt="squarefreeproof",hash_obj=hashlib.shake_256):counter=0outputs=[]#print(f"size: {size}, public_n: {public_n}, num_challenges: {num_challenges}")
whilelen(outputs)<num_challenges:#Generate Order of inputs
hash_list=[str(public_n),str(num_challenges),salt,str(counter)]#Generate String to hash with sizes
hash_list=[str(len(s))+sforsinhash_list]#Join to gether with "|"
str_input="|"+"|".join(hash_list)hash_output=hash_obj(str_input.encode("utf8")).digest(size//8)#print(f"hash_in:{str_input.encode("utf8")}, hash_out:{hash_output}")
hash_int=(int.from_bytes(hash_output,byteorder='little')%public_n)#Check that gcd(hash_int,N)=1
ifgcd(hash_int,public_n)==1:outputs.append(hash_int)counter+=1returnoutputsdefprover_part1(size,num_challenges):#Generate Key
print("Generating Key")mykey=RSA.generate(size)private_p, private_q, public_n, private_phi=[mykey.p,mykey.q,mykey.n,(mykey.p-1)*(mykey.q-1)]#Randomly generate challenges
challenges=generate_challenges(size,public_n,num_challenges)#print(f"Challenges: {challenges}")
#Calculate the inverse for the power
m=inverse(public_n,private_phi)#Calculate Proofs
# challenge ^ m % N
proofs=[pow(chal,m,public_n)forchalinchallenges]#print(f"Proofs: {proofs}")
return[public_n,proofs]defverifier_part2(size,public_n,proofs,alpha_const=319567):#Check Varables
assertpublic_n>1#Check that N is not divisible by primes smaller than 65537, or 319567
prime_product=prime_product_constant(alpha_const)assertgcd(prime_product,public_n)==1#Check that proof is greater than 0
forproofinproofs:assert(proof%public_n)>0#Generate Challenges
challenges=generate_challenges(size,public_n,len(proofs))#print(f"Challenges: {challenges}")
#Validate Challenges
foridxinrange(len(challenges)):#Compute test
tmp=pow(proofs[idx],public_n,public_n)#Challenge = proof^n
#print(f"Testing Proof {challenges[idx]}, {tmp}")
asserttmp==challenges[idx]returnTrueif__name__=='__main__':#Setup
num_challenges=5size=1024#Get challenges from Verifier
public_n, proofs=prover_part1(size,num_challenges)#Generate Profs from the challenges
valid=verifier_part2(size,public_n,proofs)print(f"Challenges have been confirmed: {valid}")
Input validation: Insure checks for valid transmitted values Implicit Trust of Prover:
- Ensure generator_g and mod_q are valid known values
- recalculate and check the random_c from the hashed values Missing Values:
- if shared_h or shared_u are missing its a major issue
- if generator_g or mod_q are missing it might be an issue Weak Randomness:
- If there are two separate verifications with the same secret_r with different data this can leak the secret_x. https://www.zkdocs.com/docs/zkdocs/zero-knowledge-protocols/schnorr/ Replay Attacks: Ensure there is a ID to both the verifier and prover to prevent replay attacks